package agent;

import java.util.ArrayList;

import operations.LoadCabadge;
import operations.LoadGoat;
import operations.LoadWolf;
import operations.Operation;
import operations.SailLeft;
import operations.SailRight;
import operations.Unload;

public class Agent {
	private static Agent instance=new Agent();
	public static final ArrayList<Operation> operationQue;
	static {
		operationQue=new ArrayList<Operation>();
		operationQue.add(new LoadCabadge());
		operationQue.add(new LoadGoat());
		operationQue.add(new LoadWolf());
		
		operationQue.add(new Unload());
		operationQue.add(new SailLeft());
		operationQue.add(new SailRight());
	}
	
	private Node root;
	public ArrayList<State> expandedList;
	private ArrayList<Node> frontier;
	
	public static Agent getInstance(){
		return instance;
	}
	private Agent() {
		root = new Node(null, new State(true, false, false, true, false, true, BoatStates.empty,true,false), 0);

		expandedList=new ArrayList<State>();
		expandedList.add(root.getState());
		frontier=new ArrayList<Node>();
		frontier.add(root);
	}
	
	private boolean goalTest(State s){
		if (s.isCabadgeLeft() && s.isGoatLeft() && s.isWolfLeft())
			return true;
//		if (s.isCabadgeRight() && s.isGoatRight() && s.isWolfRight())
//		return true;
		
		return false;
	}
	
	public void breadhFierstSearch(){
		boolean solutionFound = false;
		Node target = frontier.get(0);
		while (!frontier.isEmpty()){
			target = frontier.get(0);
			if (goalTest(target.getState())){
				solutionFound=true;
				break;
			}
			target.expand();
			expandedList.add(target.getState());
			frontier.addAll(target.getChildren());
			frontier.remove(target);
		}
		if (solutionFound)
			printSolution(target);
		else
			System.out.println("no solution found");

	}
	
	public void  depthFirstSearch() {
		boolean solutionFound = false;
		Node target = frontier.get(frontier.size()-1);
		while (!frontier.isEmpty()){
			target = frontier.get(frontier.size()-1);
			if (goalTest(target.getState())){
				solutionFound=true;
				break;
			}
			target.expand();
			expandedList.add(target.getState());
			frontier.addAll(target.getChildren());
			frontier.remove(target);
		}
		if (solutionFound)
			printSolution(target);
		else
			System.out.println("no solution found");
	}
	
	public void limitedDepthFirstSearch(){
		boolean solutionFound = false;
		Node target = frontier.get(frontier.size()-1);
		while (!frontier.isEmpty() && target.getLayer()<=5){
			target = frontier.get(frontier.size()-1);
			if (goalTest(target.getState())){
				solutionFound=true;
				break;
			}
			target.expand();
			expandedList.add(target.getState());
			frontier.addAll(target.getChildren());
			frontier.remove(target);
		}
		if (solutionFound)
			printSolution(target);
		else
			System.out.println("no solution found");
	}
	
	public void iterativeDepthFirstSearch(){
		boolean solutionFound = false;
		Node target = frontier.get(frontier.size()-1);
		while (!frontier.isEmpty() && target.getLayer()<=4){
			target = frontier.get(frontier.size()-1);
			if (goalTest(target.getState())){
				solutionFound=true;
				break;
			}
			target.expand();
			expandedList.add(target.getState());
			frontier.addAll(target.getChildren());
			frontier.remove(target);
		}
		while (!frontier.isEmpty()){
			target = frontier.get(0);
			if (goalTest(target.getState())){
				solutionFound=true;
				break;
			}
			target.expand();
			expandedList.add(target.getState());
			frontier.addAll(target.getChildren());
			frontier.remove(target);
		}
		
		
		
		if (solutionFound)
			printSolution(target);
		else
			System.out.println("no solution found");
	}
	
	public boolean alreadyExpanded(State n){
		for (State s : expandedList)
			if (s.equals(n)){
				return true;
		}
		return false;
	}
	
	public void printSolution(Node n){
		ArrayList<Node> ret=new ArrayList<Node>();
		Node p = n;
		while (p!=null){
			ret.add(p);
			p=p.getParent();
			System.out.println(p);
		}
		System.out.println("----------------------------------------");
		for (Node n2 : ret)
			System.out.println(n2);
	}
	
	private Node getLowersLayerNode(){
		Node lowest = frontier.get(0);
		for (Node n : frontier)
			if (n.getLayer()<lowest.getLayer())
				lowest=n;
		return lowest;
	}
}
